package com.cug.campus_sh_market_server.service.impl;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cug.campus_sh_market_server.entity.myenum.UserTypeEnum;
import com.cug.campus_sh_market_server.entity.param.OrderParam;
import com.cug.campus_sh_market_server.entity.param.PageParam;
import com.cug.campus_sh_market_server.service.IRestfullService;
import com.cug.campus_sh_market_server.util.MappingUtil;
import com.cug.campus_sh_market_server.util.PageUtil;
import com.cug.campus_sh_market_server.util.QueryWrapperUtil;
import com.cug.campus_sh_market_server.util.UserContext;
import com.nycreativity.elecreativity.entity.vo.PageDataVO;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author sxt
 * @version 1.0
 * @description RestfullService 接口实现
 * @date 2022/3/28 18:33
 */
public abstract class AbsRestfullServiceImpl
        <Entity, QueryParam, SaveParam, UpdateParam, VO>
        extends ServiceImpl<BaseMapper<Entity>, Entity>
        implements IRestfullService<Entity, QueryParam, SaveParam, UpdateParam, VO> {

    @Autowired
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    protected BaseMapper<VO> voBaseMapper;

    protected final Class<VO> voClass;

    protected final Class<QueryParam> queryParamClass;

    protected final Class<SaveParam> addParamClass;

    protected final Class<UpdateParam> putParamClass;


    public AbsRestfullServiceImpl() {
        voClass = currentVOClass();
        queryParamClass = currentQueryParamClass();
        addParamClass = currentAddParamClass();
        putParamClass = currentPutParamClass();
        entityClass = currentModelClass();
    }

    protected Class<QueryParam> currentQueryParamClass() {
        //noinspection unchecked
        return (Class<QueryParam>) ReflectionKit.getSuperClassGenericType(getClass(), 1);
    }

    protected Class<VO> currentVOClass() {
        //noinspection unchecked
        return (Class<VO>) ReflectionKit.getSuperClassGenericType(getClass(), 4);
    }

    protected Class<SaveParam> currentAddParamClass() {
        //noinspection unchecked
        return (Class<SaveParam>) ReflectionKit.getSuperClassGenericType(getClass(), 2);
    }

    protected Class<UpdateParam> currentPutParamClass() {
        //noinspection unchecked
        return (Class<UpdateParam>) ReflectionKit.getSuperClassGenericType(getClass(), 3);
    }

    @Override
    protected Class<Entity> currentModelClass() {
        //noinspection unchecked
        return (Class<Entity>) ReflectionKit.getSuperClassGenericType(getClass(), 0);
    }

    @Override
    @Transactional
    public boolean saveData(SaveParam saveParam) {
        Entity entity = MappingUtil.getMappingObject(saveParam, entityClass);
        return super.save(entity);
    }

    @Override
    public VO saveWithBackData(SaveParam saveParam) {
        Entity entity = MappingUtil.getMappingObject(saveParam, entityClass);
        int rs = baseMapper.insert(entity);
        if (rs > 0) {
            return voBaseMapper.selectById(getEntityId(entity));
        }
        return null;
    }

    protected String getEntityId(Entity entity) {
        Optional<Field> id = Arrays.stream(entityClass.getDeclaredFields())
                .filter(field -> field.getAnnotation(TableId.class) != null).findFirst();
        if (id.isPresent()) {
            Field field = id.get();
            try {
                field.setAccessible(true);
                return "" + field.get(entity);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return null;
    }

    @Override
    @Transactional
    public boolean removeData(Serializable id) {
        return super.removeById(id);
    }

    @Override
    @Transactional
    public boolean updateData(UpdateParam updateParam) {
        Entity entity = MappingUtil.getMappingObject(updateParam, entityClass);
        return super.updateById(entity);
    }

    @Override
    public List<VO> queryData(QueryParam param) {
        return queryData(param, null);
    }

    @Override
    public PageDataVO<VO> queryDataByPage(QueryParam param, PageParam pageParam) {
        return queryDataByPage(param, pageParam, null);
    }

    @Override
    public List<VO> queryData(QueryParam queryParam, OrderParam orderParam) {
        QueryWrapper<VO> queryWrapper = getQueryWrapper(queryParam, orderParam);
        return voBaseMapper.selectList(queryWrapper);
    }

    @Override
    public PageDataVO<VO> queryDataByPage(QueryParam queryParam, PageParam pageParam, OrderParam orderParam) {
        QueryWrapper<VO> queryWrapper = getQueryWrapper(queryParam, orderParam);
        Page<VO> selectPage = voBaseMapper.selectPage(new Page<>(pageParam.getCurrentPage(), pageParam.getPageSize()), queryWrapper);
        return PageUtil.convert(selectPage);
    }

    @Nullable
    protected QueryWrapper<VO> getQueryWrapper(QueryParam queryParam, OrderParam orderParam) {
        QueryWrapper<VO> queryWrapper = QueryWrapperUtil.getQueryWrapper(voClass, queryParamClass, queryParam);
        if (orderParam != null) {
            orderParam.orderConditions().forEach(condition -> {
                if (condition.getFirst()) {
                    queryWrapper.orderByAsc(condition.getSecond());
                } else {
                    queryWrapper.orderByDesc(condition.getSecond());
                }
            });
        }
        return queryWrapper;
    }

    protected boolean checkAdminUser() {
        Integer userType = UserContext.get().getUserType();
        return !userType.equals(UserTypeEnum.USER.getTypeCode())
                && !userType.equals(UserTypeEnum.EMPTY.getTypeCode());
    }
}
